/*
 * Copyright 2020 lcnap
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package pers.lcnap;

import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import pers.lcnap.simplerpc.rpc.NettyClient;
import pers.lcnap.simplerpc.rpc.RpcInvocationHandler;
import pers.lcnap.simplerpc.rpc.RpcRequestEvent;
import pers.lcnap.simplerpc.rpc.RpcRuntimeException;
import pers.lcnap.simplerpc.util.BeanContext;

/**
 * 暴露 客户端 接口。
 */
public class RpcClient {

    final Logger logger = LoggerFactory.getLogger(RpcClient.class);

    private boolean initialized;

    private boolean connected;

    private final NettyClient client;

    private long timeout = 10_000;

    public RpcClient() {
        client = new NettyClient();
    }

    public RpcClient(String host, int port) {
        client = new NettyClient(host, port);
    }

    /**
     * 生成代理类
     *
     * @param interfaces 接口类
     * @throws Exception
     */
    public void init(Class<?>... interfaces) throws Exception {
        for (Class<?> service : interfaces) {
            BeanContext.putRpcProxy(service, new RpcInvocationHandler(this));
        }
        initialized = true;
        logger.info("load serviceImpl {}", interfaces);
    }

    /**
     * 发起 Rpc 请求。
     *
     * @param request 请求
     * @param action  请求回调函数
     */
    public void send(RpcRequest request, GenericFutureListener<Future<RpcResponse>> action) {
        check();
        connect();
        RpcRequestEvent requestEvent = new RpcRequestEvent(request);
        if (action != null) {
            requestEvent.setListener(action);
        }
        client.sendRequestEvent(requestEvent);
    }

    public void connect() {
        if (!connected) {
            client.connect();
            connected = true;
        }
    }

    private void check() {
        if (!initialized) {
            throw new RpcRuntimeException("Not initialized.");
        }
    }

    public long getTimeout() {
        return timeout;
    }

    public void setTimeout(long timeout) {
        this.timeout = timeout;
    }

    public void shutdown() {
        client.shutdown();
    }
}
